package com.yxx.service.Impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.lang.Assert;

import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxx.constant.CommonConstant;
import com.yxx.constant.MqConstant;
import com.yxx.constant.RedisConstant;
import com.yxx.entity.SiteConfig;
import com.yxx.entity.User;
import com.yxx.entity.UserRole;
import com.yxx.entityModel.dto.MailDTO;
import com.yxx.entityModel.vo.request.CodeRequest;
import com.yxx.entityModel.vo.request.LoginRequest;
import com.yxx.entityModel.vo.request.RegisterRequest;
import com.yxx.enums.LoginTypeEnum;
import com.yxx.enums.RoleEnum;
import com.yxx.mapper.UserMapper;
import com.yxx.mapper.UserRoleMapper;
import com.yxx.service.LoginService;
import com.yxx.service.RedisService;
import com.yxx.strategy.context.LoginStrategyContext;
import com.yxx.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 登录服务接口实现类
 * @author yuxiangxun
 * @date 2025/1/19
 * @apiNote
 */
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Resource
    private UserMapper userMapper;

    //@Resource
    //private RabbitTemplate rabbitTemplate;

    @Resource
    private RedisService redisService;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private LoginStrategyContext loginStrategyContext;

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Resource
    private EmailService emailService;

    /**
     * 用户登录
     * @param login
     * @return
     */
    @Override
    public String login(LoginRequest login) {
        // 根据Login里面的用户名和密码查询用户是否存在，不存在则返回“用户不存在或密码错误”，存在则返回token
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                        .select(User::getId)
                        .eq(User::getUsername, login.getUsername())
                        .eq(User::getPassword, SecurityUtils.sha256Encrypt(login.getPassword()))
        );
        // 断言
        Assert.notNull(user,"用户不存在或密码错误");
        log.info("查看用户的登录信息：{}",user);
        // 校验指定账号是否已被封禁，如果被封禁则抛出异常 `DisableServiceException`
        StpUtil.checkDisable(user.getId());
        // 通过校验后，再进行登录
        StpUtil.login(user.getId());
        //String tokenValue = StpUtil.getTokenValue();
        //log.info("查看tokenValue值：{}",tokenValue);
        return StpUtil.getTokenValue();
    }

    /**
     * 发送邮箱验证码
     * 重点：使用到 RabbitMQ
     * @param username
     */
    @Override
    public void sendCode(String username) {
        // 断言，校验个人邮箱是否正确
        Assert.isTrue(Validator.isEmail(username),"请输入正确的邮箱！");
        // 使用胡图工具包生成随机验证码
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 6);
        String code = randomGenerator.generate();
        log.info("查看随机生成的验证码:{}",code);
        // 创建新的邮箱DTO对象
        MailDTO mailDTO = MailDTO.builder()
                .toEmail(username)
                .subject(CommonConstant.CAPTCHA)
                .content("您的验证码为："+code+"，有效期为："+ RedisConstant.CODE_EXPIRE_TIME +"请妥善保管，切勿泄露给他人。")
                .build();
        // 验证码存入消息队列中
        //rabbitTemplate.convertAndSend(MqConstant.EMAIL_EXCHANGE,MqConstant.EMAIL_SIMPLE_KEY, mailDTO);
        CompletableFuture.runAsync(() -> emailService.sendSimpleMail(mailDTO), threadPoolTaskExecutor);
        // 验证码存入 Redis 中
        redisService.setObject(RedisConstant.CODE_KEY + username, code, RedisConstant.CODE_EXPIRE_TIME, TimeUnit.MINUTES);
    }

    /**
     * 用户邮箱注册
     * @param registerRequest
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterRequest registerRequest) {
        // 校验验证码
        verifyCode(registerRequest.getUsername(),registerRequest.getCode());
        log.info("查看验证码：{}",registerRequest.getCode());
        // 接下来校验数据库中是否已经有该用户，如果有该用户证明邮箱已经被注册，则抛出异常
        User verifyUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .select(User::getUsername).eq(User::getUsername, registerRequest.getUsername()));
        Assert.isNull(verifyUser,"该邮箱已被注册，请登录");
        // 从 Redis 中获取网站配置
        SiteConfig siteConfig = redisService.getObject(RedisConstant.SITE_SETTING);
        // 如果没有该用户，则进行注册
        User newUser = User.builder()
                .nickname(CommonConstant.USER_NICKNAME)
                .username(registerRequest.getUsername())
                .password(SecurityUtils.sha256Encrypt(registerRequest.getPassword()))
                .avatar(siteConfig.getUserAvatar())
                .email(registerRequest.getUsername())
                .loginType(LoginTypeEnum.EMAIL.getLoginType())
                .isDisable(CommonConstant.FALSE)
                .build();
        // 用户表中插入新用户数据
        userMapper.insert(newUser);
        // 绑定新用户角色
        UserRole newUserRole = UserRole.builder()
                .userId(newUser.getId())
                .roleId(RoleEnum.USER.getRoleId())
                .build();
        // 用户角色表中插入新用户角色数据
        userRoleMapper.insert(newUserRole);
    }

    /**
     * Gitee 登录
     * @param codeRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String giteeLogin(CodeRequest codeRequest) {
        return loginStrategyContext.executeLoginStrategy(codeRequest, LoginTypeEnum.GITEE);
    }

    /**
     * Github 登录
     * @param data
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String githubLogin(CodeRequest data) {
        return loginStrategyContext.executeLoginStrategy(data,LoginTypeEnum.GITHUB);
    }

    /**
     * QQ 登录
     * @param data
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String qqLogin(CodeRequest data) {
        return loginStrategyContext.executeLoginStrategy(data,LoginTypeEnum.QQ);
    }

    /**
     * 校验验证码
     * @param username
     * @param code
     */
    public void verifyCode(String username, String code){
        // 从 Redis 中获取验证码
        String systemCode = redisService.getObject(RedisConstant.CODE_KEY + username);
        log.info("验证码为：{}",systemCode);
        // 断言，如果验证码为空（验证码过期），则抛出异常
        Assert.notBlank(systemCode,"验证码未发送或已过期");
        // 断言，用户输入的验证码和 Redis 中的验证码进行比较，如果不相等，则抛出异常
        Assert.isTrue(systemCode.equals(code), "验证码错误，请重新输入");
    }


}
